package il.ac.technion.wiki.impl.wikidocument.content;

import il.ac.technion.wiki.api.wikidocument.content.ContentImporter;
import il.ac.technion.wiki.impl.logger.LogFactory;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.InvalidObjectException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.log4j.Logger;
import org.xml.sax.SAXException;

/**
 * You may import <strong>valid</strong> HTML content in two ways: through a String or through InputStream.
 */
public class HTMLContentImporter extends ContentImporter {
	Logger logger = LogFactory.getLog(this.getClass());

	/**
	 * Import HTML content with String
	 * An Exception is thrown is your HTML is not valid, or if there were a problem reading your input.
	 */
	public HTMLContentImporter(String htmlString) throws IOException {
		if (htmlString == null) {
			logger.error("null parameter");
			throw new IllegalArgumentException("null parameter");
		}
		handleString(htmlString);
	}

	/**
	 * Import HTML content with InputStream
	 * An Exception is thrown is your HTML is not valid, or if there were a problem reading your input.
	 */
	public HTMLContentImporter(InputStream is) throws IOException {
		if (is == null) {
			logger.error("null parameter");
			throw new IllegalArgumentException("null parameter");
		}
		handleStream(is);
	}

	private void handleString(String htmlString) throws InvalidObjectException {
		generateDOM(new ByteArrayInputStream(htmlString.getBytes()));
		updateContent(htmlString);
	}

	private void handleStream(InputStream is) throws IOException {
		try {
			// duplicate the stream for multiple uses
			ByteArrayOutputStream baos = readStream(is);
			
			// validate html
			generateDOM(new ByteArrayInputStream(baos.toByteArray()));
			
			String htmlString = getStringFromStream(new ByteArrayInputStream(
					baos.toByteArray()));
			updateContent(htmlString);
		} catch (InternalError e) {
			logger.error("InternalError", e);
			content = null;
		}
	}

	private void updateContent(String htmlString) {
		content = htmlString.replaceAll("\\</.*?>", " ")
				.replaceAll("\\<.*?>", "").trim();
	}

	private ByteArrayOutputStream readStream(InputStream is) throws IOException {
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		byte[] buffer = new byte[1024];
		int len;
		while ((len = is.read(buffer)) > 0) {
			baos.write(buffer, 0, len);
		}
		baos.flush();
		return baos;
	}

	private String getStringFromStream(InputStream is) throws IOException {
		BufferedReader reader = new BufferedReader(new InputStreamReader(is));
		String line = "", $ = "";
		while ((line = reader.readLine()) != null) {
			$ += line;
		}
		reader.close();
		return $;
	}

	private void generateDOM(InputStream is) throws InvalidObjectException {
		try {
			DocumentBuilder builder = DocumentBuilderFactory.newInstance()
					.newDocumentBuilder();
			builder.parse(is);
		} catch (ParserConfigurationException e) {
			logger.error("can't create the DOM builder from the factory");
			throw new InternalError(
					"can't create the DOM builder from the factory");
		} catch (SAXException e) {
			logger.error("couldn't create parse the given input stream", e);
			throw new IllegalArgumentException(
					"couldn't create parse the given input stream");
		} catch (IOException e) {
			logger.error(
					"IOException was thrown when tried to create DOM document from the input stream",
					e);
			throw new InvalidObjectException(
					"IOException was thrown when tried to create DOM document from the input stream");
		}
	}
}
